{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 多线程"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "'''\n",
    "\n",
    "线程可以分为:\n",
    "\n",
    "内核线程：由操作系统内核创建和撤销。\n",
    "用户线程：不需要内核支持而在用户程序中实现的线程。\n",
    "\n",
    "\n",
    "Python3 线程中常用的两个模块为：\n",
    "\n",
    "_thread\n",
    "threading(推荐使用)\n",
    "thread 模块已被废弃。用户可以使用 threading 模块代替。所以，在 Python3 中不能再使用\"thread\" 模块。为了兼容性，Python3 将 thread 重命名为 \"_thread\"。\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "Python中使用线程有两种方式：函数或者用类来包装线程对象。\n",
    "\n",
    "函数式：调用 _thread 模块中的start_new_thread()函数来产生新线程。语法如下:\n",
    "\n",
    "_thread.start_new_thread ( function, args[, kwargs] )\n",
    "参数说明:\n",
    "\n",
    "function - 线程函数。\n",
    "args - 传递给线程函数的参数,他必须是个tuple类型。\n",
    "kwargs - 可选参数。\n",
    "\n",
    "\n",
    "'''"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "thread-1 : Thu Aug  8 17:50:51 2019\n",
      "thread-2 : Thu Aug  8 17:50:53 2019\n",
      "thread-1 : Thu Aug  8 17:50:53 2019\n",
      "thread-1 : Thu Aug  8 17:50:55 2019\n",
      "thread-2 : Thu Aug  8 17:50:57 2019\n",
      "thread-1 : Thu Aug  8 17:50:57 2019\n",
      "thread-1 : Thu Aug  8 17:50:59 2019\n",
      "thread-2 : Thu Aug  8 17:51:01 2019\n",
      "thread-2 : Thu Aug  8 17:51:05 2019\n",
      "thread-2 : Thu Aug  8 17:51:09 2019\n"
     ]
    }
   ],
   "source": [
    "import _thread\n",
    "import time\n",
    "\n",
    "# 为线程定义一个函数\n",
    "def print_time(threadName, delay):\n",
    "    count = 0\n",
    "    while count < 5:\n",
    "        time.sleep(delay)\n",
    "        count += 1\n",
    "        print('%s : %s' % (threadName,time.ctime(time.time())))\n",
    "        \n",
    "# 创建两个线程\n",
    "\n",
    "try:\n",
    "    _thread.start_new_thread(print_time,('thread-1',1,))\n",
    "    _thread.start_new_thread(print_time,('thread-2',2,))\n",
    "    \n",
    "except:\n",
    "    \n",
    "    print('error 无法启动线程')\n",
    "\n",
    "    \n",
    "while 1:\n",
    "    pass\n",
    "   \n",
    " "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "'''\n",
    "\n",
    "线程模块\n",
    "Python3 通过两个标准库 _thread 和 threading 提供对线程的支持。\n",
    "\n",
    "_thread 提供了低级别的、原始的线程以及一个简单的锁，它相比于 threading 模块的功能还是比较有限的。\n",
    "\n",
    "threading 模块除了包含 _thread 模块中的所有方法外，还提供的其他方法：\n",
    "\n",
    "threading.currentThread(): 返回当前的线程变量。\n",
    "threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前，不包括启动前和终止后的线程。\n",
    "threading.activeCount(): 返回正在运行的线程数量，与len(threading.enumerate())有相同的结果。\n",
    "除了使用方法外，线程模块同样提供了Thread类来处理线程，Thread类提供了以下方法:\n",
    "\n",
    "run(): 用以表示线程活动的方法。\n",
    "start():启动线程活动。\n",
    "join([time]): 等待至线程中止。这阻塞调用线程直至线程的join() 方法被调用中止-正常退出或者抛出未处理的异常-或者是可选的超时发生。\n",
    "isAlive(): 返回线程是否活动的。\n",
    "getName(): 返回线程名。\n",
    "setName(): 设置线程名。\n",
    "\n",
    "\n",
    "'''"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "开始线程：Thread-1\n",
      "开始线程：Thread-2\n",
      "Thread-1: Thu Aug  8 18:01:37 2019\n",
      "Thread-1: Thu Aug  8 18:01:38 2019\n",
      "Thread-2: Thu Aug  8 18:01:38 2019\n",
      "Thread-1: Thu Aug  8 18:01:39 2019\n",
      "Thread-1: Thu Aug  8 18:01:40 2019\n",
      "Thread-2: Thu Aug  8 18:01:40 2019\n",
      "Thread-1: Thu Aug  8 18:01:41 2019\n",
      "退出线程：Thread-1\n",
      "Thread-2: Thu Aug  8 18:01:42 2019\n",
      "Thread-2: Thu Aug  8 18:01:44 2019\n",
      "Thread-2: Thu Aug  8 18:01:46 2019\n",
      "退出线程：Thread-2\n",
      "退出主线程\n"
     ]
    }
   ],
   "source": [
    "'''\n",
    "\n",
    "使用 threading 模块创建线程\n",
    "我们可以通过直接从 threading.Thread 继承创建一个新的子类，并实例化后调用 start() 方法启动新线程，即它调用了线程的 run() 方法：\n",
    "\n",
    "\n",
    "'''\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "import threading\n",
    "import time\n",
    "\n",
    "exitFlag = 0\n",
    "class myThread (threading.Thread):\n",
    "    def __init__(self, threadID, name, counter):\n",
    "        threading.Thread.__init__(self)\n",
    "        self.threadID = threadID\n",
    "        self.name = name\n",
    "        self.counter = counter\n",
    "    def run(self):\n",
    "        print (\"开始线程：\" + self.name)\n",
    "        print_time(self.name, self.counter, 5)\n",
    "        print (\"退出线程：\" + self.name)\n",
    "\n",
    "def print_time(threadName, delay, counter):\n",
    "    while counter:\n",
    "        if exitFlag:\n",
    "            threadName.exit()\n",
    "        time.sleep(delay)\n",
    "        print (\"%s: %s\" % (threadName, time.ctime(time.time())))\n",
    "        counter -= 1\n",
    "\n",
    "# 创建新线程\n",
    "thread1 = myThread(1, \"Thread-1\", 1)\n",
    "thread2 = myThread(2, \"Thread-2\", 2)\n",
    "\n",
    "# 开启新线程\n",
    "thread1.start()\n",
    "thread2.start()\n",
    "thread1.join()\n",
    "thread2.join()\n",
    "print (\"退出主线程\")\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "开启线程：线程 - 1 \n",
      "开启线程：线程 - 2\n",
      "线程 - 1 : Fri Aug  9 10:05:37 2019\n",
      "线程 - 1 : Fri Aug  9 10:05:38 2019\n",
      "线程 - 2: Fri Aug  9 10:05:38 2019\n",
      "线程 - 1 : Fri Aug  9 10:05:39 2019\n",
      "线程 - 2: Fri Aug  9 10:05:40 2019\n",
      "线程 - 1 : Fri Aug  9 10:05:40 2019\n",
      "线程 - 1 : Fri Aug  9 10:05:41 2019\n",
      "退出线程：线程 - 1 \n",
      "线程 - 2: Fri Aug  9 10:05:42 2019\n",
      "线程 - 2: Fri Aug  9 10:05:44 2019\n",
      "线程 - 2: Fri Aug  9 10:05:46 2019\n",
      "退出线程：线程 - 2\n",
      "退出主线程\n"
     ]
    }
   ],
   "source": [
    "import threading\n",
    "import time\n",
    "exitFlag = 0\n",
    "class myThread(threading.Thread):\n",
    "    def __init__(self, threadID, name, counter):\n",
    "        threading.Thread.__init__(self)\n",
    "        self.threadID = threadID\n",
    "        self.name = name\n",
    "        self.counter = counter\n",
    "    def run(self):\n",
    "        print(\"开启线程：\"+self.name)\n",
    "        print_time(self.name,self.counter,5)\n",
    "        print(\"退出线程：\"+self.name)\n",
    "        \n",
    "def print_time(threadName,delay,counter):\n",
    "    while counter:\n",
    "        if exitFlag:\n",
    "            threadName.exit()\n",
    "        time.sleep(delay)\n",
    "        print(\"%s: %s\"% (threadName, time.ctime(time.time())))\n",
    "        counter -= 1\n",
    "        \n",
    "# 创建新线程\n",
    "\n",
    "thread1 = myThread(1,'线程 - 1 ',1)\n",
    "thread2 = myThread(2,'线程 - 2',2)\n",
    "\n",
    "#开启线程\n",
    "thread1.start()\n",
    "thread2.start()\n",
    "thread1.join()\n",
    "thread2.join()\n",
    "\n",
    "print('退出主线程')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 线程同步"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "'''\n",
    "\n",
    "\n",
    "如果多个线程共同对某个数据修改，则可能出现不可预料的结果，为了保证数据的正确性，需要对多个线程进行同步。\n",
    "\n",
    "使用Thread对象的Lock和Rlock可以实现简单的线程同步，这两个对象都有acquire方法和release方法，对于那些需要每次只允许一个线程操作的数据，可以将其操作放到acquire和release方法之间。如下：\n",
    "\n",
    "多线程的优势在于可以同时运行多个任务（至少感觉起来是这样）。但是当线程需要共享数据时，可能存在数据不同步的问题。\n",
    "\n",
    "考虑这样一种情况：一个列表里所有元素都是0，线程\"set\"从后向前把所有元素改成1，而线程\"print\"负责从前往后读取列表并打印。\n",
    "\n",
    "那么，可能线程\"set\"开始改的时候，线程\"print\"便来打印列表了，输出就成了一半0一半1，这就是数据的不同步。为了避免这种情况，引入了锁的概念。\n",
    "\n",
    "锁有两种状态——锁定和未锁定。每当一个线程比如\"set\"要访问共享数据时，必须先获得锁定；如果已经有别的线程比如\"print\"获得锁定了，那么就让线程\"set\"暂停，也就是同步阻塞；等到线程\"print\"访问完毕，释放锁以后，再让线程\"set\"继续。\n",
    "\n",
    "经过这样的处理，打印列表时要么全部输出0，要么全部输出1，不会再出现一半0一半1的尴尬场面。 \n",
    "\n",
    "\n",
    "'''"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Starting Thread-1\n",
      "Starting Thread-2\n",
      "Thread-1: Fri Aug  9 10:51:19 2019\n",
      "Thread-1: Fri Aug  9 10:51:20 2019\n",
      "Thread-1: Fri Aug  9 10:51:21 2019\n",
      "Exiting Main Thread\n",
      "Thread-2: Fri Aug  9 10:51:23 2019\n",
      "Thread-2: Fri Aug  9 10:51:25 2019\n",
      "Thread-2: Fri Aug  9 10:51:27 2019\n",
      "Exiting Main Thread\n"
     ]
    }
   ],
   "source": [
    "import threading\n",
    "import time\n",
    " \n",
    "class myThread (threading.Thread):\n",
    "    def __init__(self, threadID, name, counter):\n",
    "        threading.Thread.__init__(self)\n",
    "        self.threadID = threadID\n",
    "        self.name = name\n",
    "        self.counter = counter\n",
    "    def run(self):\n",
    "        print(\"Starting \" + self.name)\n",
    "       # 获得锁，成功获得锁定后返回True\n",
    "       # 可选的timeout参数不填时将一直阻塞直到获得锁定\n",
    "       # 否则超时后将返回False\n",
    "        threadLock.acquire()\n",
    "        print_time(self.name, self.counter, 3)\n",
    "        # 释放锁\n",
    "        threadLock.release()\n",
    " \n",
    "    def print_time(threadName, delay, counter):\n",
    "        while counter:\n",
    "            time.sleep(delay)\n",
    "            print(\"%s: %s\" % (threadName, time.ctime(time.time())))\n",
    "            counter -= 1\n",
    "\n",
    "threadLock = threading.Lock()\n",
    "threads = []\n",
    " \n",
    "# 创建新线程\n",
    "thread1 = myThread(1, \"Thread-1\", 1)\n",
    "thread2 = myThread(2, \"Thread-2\", 2)\n",
    " \n",
    "# 开启新线程\n",
    "thread1.start()\n",
    "thread2.start()\n",
    " \n",
    "# 添加线程到线程列表\n",
    "threads.append(thread1)\n",
    "threads.append(thread2)\n",
    " \n",
    "# 等待所有线程完成\n",
    "for t in threads:\n",
    "    t.join()\n",
    "    print(\"Exiting Main Thread\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 线程优先级队列（ Queue）"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "'''\n",
    "\n",
    "Python的Queue模块中提供了同步的、线程安全的队列类，包括FIFO（先入先出)队列Queue，LIFO（后入先出）队列LifoQueue，和优先级队列PriorityQueue。这些队列都实现了锁原语，能够在多线程中直接使用。可以使用队列来实现线程间的同步。\n",
    "\n",
    "Queue模块中的常用方法:\n",
    "\n",
    "Queue.qsize() 返回队列的大小\n",
    "Queue.empty() 如果队列为空，返回True,反之False\n",
    "Queue.full() 如果队列满了，返回True,反之False\n",
    "Queue.full 与 maxsize 大小对应\n",
    "Queue.get([block[, timeout]])获取队列，timeout等待时间\n",
    "Queue.get_nowait() 相当Queue.get(False)\n",
    "Queue.put(item) 写入队列，timeout等待时间\n",
    "Queue.put_nowait(item) 相当Queue.put(item, False)\n",
    "Queue.task_done() 在完成一项工作之后，Queue.task_done()函数向任务已经完成的队列发送一个信号\n",
    "Queue.join() 实际上意味着等到队列为空，再执行别的操作\n",
    "\n",
    "'''"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import sys\n",
    "\n",
    "if sys.version > '3':\n",
    "    import queueLock\n",
    "else:\n",
    "    import Queue\n",
    "    \n",
    "import threading\n",
    "import time\n",
    " \n",
    "exitFlag = 0\n",
    " \n",
    "class myThread (threading.Thread):\n",
    "    def __init__(self, threadID, name, q):\n",
    "        threading.Thread.__init__(self)\n",
    "        self.threadID = threadID\n",
    "        self.name = name\n",
    "        self.q = q\n",
    "    def run(self):\n",
    "        print(\"Starting \" + self.name)\n",
    "        process_data(self.name, self.q)\n",
    "        print(\"Exiting \" + self.name)\n",
    "        \n",
    "def process_data(threadName, q):\n",
    "    while not exitFlag:\n",
    "        queueLock.acquire()\n",
    "        if not workQueue.empty():\n",
    "            data = q.get()\n",
    "            queueLock.release()\n",
    "            print(\"%s processing %s\" % (threadName, data))\n",
    "        else:\n",
    "            queueLock.release()\n",
    "        time.sleep(1)\n",
    "        \n",
    "threadList = [\"Thread-1\", \"Thread-2\", \"Thread-3\"]\n",
    "nameList = [\"One\", \"Two\", \"Three\", \"Four\", \"Five\"]\n",
    "queueLock = threading.Lock()\n",
    "if sys.version > '3':\n",
    "    workQueue = queue.Queue(10)\n",
    "else:\n",
    "      workQueue = Queue.Queue(10)\n",
    "threads = []\n",
    "threadID = 1\n",
    " \n",
    "# 创建新线程\n",
    "for tName in threadList:\n",
    "    thread = myThread(threadID, tName, workQueue)\n",
    "    thread.start()\n",
    "    threads.append(thread)\n",
    "    threadID += 1\n",
    "    \n",
    "# 填充队列\n",
    "queueLock.acquire()\n",
    "for word in nameList:\n",
    "    workQueue.put(word)\n",
    "queueLock.release()\n",
    " \n",
    "# 等待队列清空\n",
    "while not workQueue.empty():\n",
    "    pass\n",
    " \n",
    "# 通知线程是时候退出\n",
    "exitFlag = 1\n",
    " \n",
    "# 等待所有线程完成\n",
    "for t in threads:\n",
    "    t.join()\n",
    "print(\"Exiting Main Thread\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.1"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
